package com.societyGames.robotExercises
{
import com.societyGames.action.ActionQueue;

import flash.geom.Point;

public class RobotSimulation
{
  public var showNumbers:Boolean;
  public var robotX:int;
  public var robotY:int;
  public var homeX:int = int.MIN_VALUE;
  public var homeY:int = int.MIN_VALUE;
  public var spaceText:String; //A text representation of blocked spots. O means blocked
  public const gridWidth:int = 5;
  public const gridHeight:int = 5;

  private const _spaceCharacter:String = "0";
  private var _render:RobotRender;
  private var _homePosition:Point;
  private var _robotPosition:Point = new Point();
  private var _robotStartPosition:Point;
  private var _spaces:Vector.<Vector.<Space>>;

  public function initialize(actionQueue:ActionQueue):void
  {
    createSpaces();
    this._render = new RobotRender(this, actionQueue);

    placeHome();
    placeHoles();

    if (this.showNumbers)
    {
      this._render.showNumbers();
    }

    placeRobotExplicit(this.robotX, this.robotY);
  }

  private function createSpaces():void
  {
    _spaces = new Vector.<Vector.<Space>>();
    for (var x:int = 0; x < gridWidth; x++)
    {
      var column:Vector.<Space> = new Vector.<Space>();
      _spaces.push(column);
      for (var y:int = 0; y < gridHeight; y++)
      {
        column.push(new Space(Space.NORMAL));
      }
    }
  }

  private function placeHome():void
  {
    if (this.homeX == int.MIN_VALUE || this.homeY == int.MIN_VALUE)
    {
      _homePosition = new Point(Math.floor(gridWidth / 2), Math.floor(gridHeight / 2));
    }
    else
    {
      _homePosition = new Point(homeX, homeY);
    }
    this._spaces[_homePosition.x][_homePosition.y].type = Space.HOME;
    this._render.setHome(this._homePosition);
  }

  private function placeHoles():void
  {
    if (spaceText != null)
    {
      if (spaceText.length > gridWidth * gridHeight)
      {
        throw new Error("blockText is invalid.");
      }

      var index:int = 0;
      for (var y:int = 0; y < gridHeight; y++)
      {
        for (var x:int = 0; x < gridWidth; x++)
        {
          if (spaceText.substr(index, 1) == _spaceCharacter)
          {
            this._spaces[x][y].type = Space.WALL;
            this._render.placeWall(x, y);
          }
          index++;
        }
      }
    }
  }

  public function get robotPosition():Point
  {
    return this._robotPosition;
  }

  public function get homePosition():Point
  {
    return this._homePosition;
  }

  public function get render():RobotRender
  {
    return this._render;
  }

  public function placeRobot(randomize:Boolean):void
  {
    if (randomize || this._robotStartPosition == null)
    {
      if (this._robotStartPosition == null)
      {
        this._robotStartPosition = new Point();
      }
      //Choose a new starting location.
      var randomNumber:Number = Math.random();
      if (randomNumber < 0.25)
      {
        this._robotStartPosition.x = 0;
        this._robotStartPosition.y = Math.floor(Math.random() * gridHeight);
      }
      else if (randomNumber < 0.5)
      {
        this._robotStartPosition.x = this.gridWidth - 1;
        this._robotStartPosition.y = Math.floor(Math.random() * gridHeight);
      }
      else if (randomNumber < 0.75)
      {
        this._robotStartPosition.x = Math.floor(Math.random() * gridWidth);
        this._robotStartPosition.y = 0;
      }
      else if (randomNumber < 1)
      {
        this._robotStartPosition.x = Math.floor(Math.random() * gridWidth);
        this._robotStartPosition.y = this.gridHeight - 1;
      }
    }
    placeRobotAtStartPosition();
  }

  public function placeRobotExplicit(robotX:int, robotY:int):void
  {
    this._robotStartPosition = new Point(robotX, robotY);
    placeRobotAtStartPosition();
  }

  public function setHomePosition(homeX:int, homeY:int):void
  {
    this._homePosition = new Point(homeX, homeY);
    this._render.setHome(this._homePosition);
  }

  private function placeRobotAtStartPosition():void
  {
    this._robotPosition.x = this._robotStartPosition.x;
    this._robotPosition.y = this._robotStartPosition.y;
    this._spaces[_robotPosition.x][_robotPosition.y].hasVisited = true;
    this._render.setRobot(this._robotPosition);
  }

  public function get isRobotHome():Boolean
  {
    return this._robotPosition.equals(this._homePosition);
  }

  private function updateRobotPosition(dx:int, dy:int):void
  {
    this._robotPosition.x += dx;
    this._robotPosition.y += dy;
    this._render.moveRobot(this._robotPosition.x, this._robotPosition.y);
  }

  public function move(dx:int, dy:int):void
  {
    updateRobotPosition(dx, dy);
  }

  private function moveDiscrete(dx:int, dy:int):void
  {
    var endX:int = _robotPosition.x + dx;
    var endY:int = robotPosition.y + dy;
    if (isBlocked(endX, endY))
    {
      this._render.moveIllegal(this._robotPosition.x,
              this._robotPosition.y,
              endX,
              endY
      );
    }
    else
    {
      _spaces[endX][endY].hasVisited = true;
      updateRobotPosition(dx, dy);
    }
  }

  public function moveRobotRight():void
  {
    moveDiscrete(1, 0);
  }

  public function moveRobotLeft():void
  {
    moveDiscrete(-1, 0);
  }

  public function moveRobotUp():void
  {
    moveDiscrete(0, -1);
  }

  public function moveRobotDown():void
  {
    moveDiscrete(0, 1);
  }

  public function isBlocked(x:int, y:int):Boolean
  {
    return x < 0 || x >= gridWidth || y < 0 || y >= gridHeight || this._spaces[x][y].type == Space.WALL;
  }

  public function hasVisited(x:int, y:int):Boolean
  {
    return x >= 0 && x < this.gridWidth &&
            y >= 0 && y < this.gridHeight &&
            _spaces[x][y].hasVisited;
  }
}
}
